home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1995 April / Internet Tools.iso / infoserv / gopher / Unix / GopherTools / glog / glog35.c.Z / glog35.c
Encoding:
C/C++ Source or Header  |  1994-12-12  |  46.3 KB  |  1,793 lines

  1. /*** glog35.c -- analysis tool for Unix gopherd logs ***/
  2. /******************** START CONFIGURATION ***************************/
  3.  
  4. /* Define NODETAIL if you don't want detail listings to be kept. Detail 
  5.  * listings can double the pointer memory required and slow things down.
  6.  */
  7.  
  8. /* #define NODETAIL    */  /* uncomment if you DON'T want detail listings */
  9. /* #define DETAIL_TOTAL*/  /* uncomment if you WANT detail percentages to 
  10.                   be out out the total number of connections  */
  11. /* #define NOSEARCHTXT */  /* uncomment if you DON'T want to output WHAT  */
  12.                            /*    was searched for                         */
  13. /* #define HCASEMATTERS */ /* uncomment if case matters for hostnames     */
  14.  
  15. #define MAXGLINESIZE    500 /* Maximum length of a gopher log line */
  16. /******************** END CONFIGURATION ***************************/
  17. /* That was really hard wasn't it :) */
  18.  
  19. /* Bug Reports/suggestions goto */
  20. #define EMAIL "awick@csugrad.cs.vt.edu"
  21.  
  22. #define    GLOG_VERSION "Gopher Log Analyzer 3.5"
  23.  
  24. /* Version 3.5 ?/?/94 awick
  25.  * Added Domain Reports
  26.  * Added -s? sort of reports and histograms, not plots though
  27.  * Changed some shorts to ints
  28.  * Lowers the case of all hostnames now, unless above uncommented
  29.  * Only keep track of reports need, saves memory and time.
  30.  * Increased speed of adding data (special InsertDates now)
  31.  *
  32.  * Version 3.4 4/4/94 awick
  33.  * Improved coding efficiency in ProcessLine() routine
  34.  * Fixed bug when identifying NOPASV ftp types (previously they went
  35.  *  into the error log). If you use the NOPASV patch, you know what I mean.
  36.  * Search Type now shows what word(s) was(were) searched for...UNLESS
  37.  *  NOSEARCHTXT defined
  38.  * Cleaned up the code alot, and changed the way reports are produced
  39.  * Added the -g histograms option
  40.  * Added the -a averaging/estimating stuff
  41.  * Added the -i infile support
  42.  * Added the -o outfile support
  43.  * Added support for VMS dennis_sherman@unc.edu
  44.  *
  45.  * Version 3.3 7/20/93 jdc@selway.umt.edu
  46.  * fixed up main() routine so that: errors on the command line (or -h as
  47.  * first parameter) cause glog to print the help information and abort.
  48.  * fixed up PrintHelp() routine so help message is more understandable
  49.  * changed FILETYPE character from ' ' to 'I' (What about Image?)
  50.  *
  51.  * Version 3.2 
  52.  * Fixed a small bug with search
  53.  *
  54.  * Version 3.1  7/11/93 Andy Wick - awick@csugrad.cs.vt.edu
  55.  * Added supported for the "missing" gopher types, time plots, month
  56.  * reports and several other things that I forgot :)
  57.  * Also added -b and -e options.
  58.  *
  59.  * Version 3.0 
  60.  * by: Andy Wick - awick@csugrad.cs.vt.edu
  61.  * This version is an almost TOTAL rewrite of glog.  It now reads all
  62.  * the information into memory before it does ANYTHING.  It then goes
  63.  * through the arguments one at a time.  So in order to effect something
  64.  * you must change it before the report.  ie.  Argument order matters now.
  65.  *
  66.  * Version 2.2
  67.  * by: Chuck Shotton - cshotton@oac.hsc.uth.tmc.edu
  68.  *
  69.  * Version 2.1 12/29.92
  70.  * by: Michael Mealling - Georgia Institute of Technology
  71.  *       Office of Information Technology
  72.  *       michael.meallingl@oit.gatech.edu
  73.  *
  74.  * Versions 1.0 6/17/92
  75.  * by: Chuck Shotton - U of Texas Health Science Center - Houston,
  76.  *    Office    of Academic Computing
  77.  *    cshotton@oac.hsc.uth.tmc.edu
  78.  */
  79.  
  80.  
  81. #include <stdio.h>
  82. #include <ctype.h>
  83. #include <string.h>
  84. /* Some machines don't have a stdlib.  You can remove it if need be
  85.  *  it is here for type checking, usually :) 
  86.  */
  87. #include <stdlib.h>
  88. #ifdef THINK_C
  89. #include <console.h> 
  90. #endif
  91.  
  92. /********************** THINGS THAT EFFECT INPUT/OUTPUT ********************/
  93.  
  94. /* These are the different types of data that are currently reconized. 
  95.    Each must be unique and it only effects the output. */
  96. #define FILETYPE    'I'
  97. #define BINARYTYPE    'B'
  98. #define SOUNDTYPE    'S'
  99. #define DIRTYPE        'D'
  100. #define MAILDIRTYPE    'M'
  101. #define FTPTYPE        'F'
  102. #define RANGETYPE    'R'
  103. #define SEARCHTYPE    '?'
  104.  
  105. char     *ROOTNAME = "Root Connections";
  106. char     *AverageStrs[6] = {""," per year"," per month"," per week"," per day",
  107.             " per hour"};
  108.  
  109. /* How they are in your gopher log file */
  110. char    Days[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  111. char    Months[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  112.                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  113.  
  114. /* The base file name for gnuplot reports */
  115. char DEFAULTBASE[7] = "gopher"; 
  116. /**********************************************************************/
  117.  
  118. /* GENERAL STUFF */
  119. typedef unsigned char byte;
  120.  
  121. /* Error log link list */
  122. typedef    struct enode_list {
  123.     char      *data;
  124.     struct enode_list *next;
  125. } ELIST_REC, *ELIST_PTR;
  126.  
  127. /* GOPHER LINE STUFF */
  128.  
  129. /* One line of the gopher log is stored in here */
  130. typedef struct gopher_line {
  131.    byte     day;
  132.    byte     month;
  133.    byte        hour;
  134.    byte     date;
  135.    char     *hostname;
  136.    char        *domainname;
  137.    char     *path;
  138.    char     type;
  139. } GOPHER_REC, *GOPHER_PTR;
  140.  
  141. /* A Linked list of gopher lines */
  142. typedef    struct node_list {
  143.     GOPHER_PTR      data;
  144.         long int      hits;
  145.     struct node_list *next;
  146. }  LIST_REC, *LIST_PTR;
  147.  
  148. /* Main tree */
  149. typedef    struct node_rec    {
  150.     GOPHER_PTR    data;
  151. #ifndef NODETAIL
  152.     LIST_REC     *llist;
  153. #endif
  154.         long int     hits;
  155.     struct node_rec    *left, *right;
  156. } NODE_REC, *NODE_PTR;
  157.  
  158. /* The cruft list is a general list for things that aren't parse-able by
  159.  * ProcessLine().  "cruft" kept for historical reasons.
  160.  */
  161. ELIST_PTR     cruft = NULL;
  162.  
  163. /*
  164.  * The following lists are maintained.
  165.  */
  166. NODE_PTR     hosts = NULL;
  167. NODE_PTR     docs  = NULL;
  168. NODE_PTR     dais  = NULL; /* Can't be days because of VMS :) */
  169. NODE_PTR     dates = NULL;
  170. NODE_PTR     types = NULL;
  171. NODE_PTR     hours = NULL;
  172. NODE_PTR     domains = NULL;
  173.  
  174. long int     TotalConnects = 0;
  175.  
  176. char    *in_file_name = NULL; /* NULL if stdin */
  177.  
  178. /* Used to tell the Info routines that you are starting and stoping */
  179. #define STARTINFO    (GOPHER_PTR)0
  180. #define ENDINFO        (GOPHER_PTR)1
  181.  
  182. /* Width of reports, Width is a major hack, so be careful */
  183. int     Width     =     62; /* 79 - WIDTHSUB */
  184. #define WIDTHSUB     17  /* The width of the standard print stuff */
  185.  
  186. /* Information */
  187.    /* Internal */
  188. #define NOINFO        0
  189. #define DETAILINFO    1
  190.    /* Changing these will change the command line options also */
  191.    /* Error log requested, but not a valid SORT TYPE */
  192. #define ERRORINFO    'E'
  193.    /* SORT TYPES */
  194. #define DATAINFO     'D'
  195. #define HOSTINFO    'H'
  196. #define WEEKDAYINFO    'W'
  197. #define MONTHDATEINFO    'M'
  198. #define TYPEINFO    'T'
  199. #define TIMEINFO    'I'
  200. #define DOMAININFO    'O'
  201.  
  202. byte do_D=0,do_H=0, do_W=0, do_T=0, do_I=0, do_O=0;
  203.  
  204. char *base = DEFAULTBASE;
  205.  
  206. /* Start stop dates */
  207. char start_date[13], stop_date[13];
  208.  
  209. /* Start stop months */
  210. byte mbegin = 1, mend = 12;
  211.  
  212. /* Averages */
  213. byte AverageStrPos = 0; /* Everything */
  214. float AverageDiv = 1.0; /* Don't change the value */
  215.  
  216. /* Sorting */
  217.  
  218. int SortMult = 1; /*Forward Sort, -1 = Reverse Sort */
  219. byte SortHits = 1; /*Sort by Hits = 1, Names = 0 */
  220.  
  221. /* The only forward decleration needed, since I wrote this the pascal way,
  222.  * the way all programs should be written. :) You don't need all the stupid
  223.  * forward declerations, or prototypes.
  224.  */
  225. void PrintInfo(NODE_PTR tree, void print(GOPHER_PTR), int cmp(GOPHER_PTR a, GOPHER_PTR b), byte DetailType);
  226.  
  227. /* Simple define that cleans up the code a little */
  228. #define INFOERROR(str, strparam) {fprintf(stderr, (str), (strparam)); exit(1);}
  229. #define EXITERROR(str) {fprintf(stderr, (str)); fflush(stderr); exit(1);}
  230. #define HELPERROR(str) {fprintf(stderr, (str)); PrintHelp(stderr,0);}
  231.  
  232. #ifndef MIN
  233. #define MIN(a,b) ((a)<(b)?(a):(b))
  234. #endif
  235. /*******************************/
  236. /* My StrStr, since it is not standard on all unix machines (sun 4.0.3) */
  237. char *mystrstr(char *s1, char *s2)
  238. {
  239. register int len;
  240.  
  241.    if((len = strlen(s2)) == 0)
  242.       return s1;
  243.  
  244.    while (*s1 != '\0')
  245.    {
  246.       if (strncmp(s1, s2, len) == 0)
  247.          return s1;
  248.       s1++;
  249.    }
  250.  
  251.    return NULL;
  252. }
  253. /*******************************/
  254. void mytolower(char *s)
  255. {
  256.    for(; *s != 0;s++)
  257.       if (isupper(*s))
  258.      *s += 32;
  259. }
  260. /*******************************/
  261. /* Add item to error log */
  262. ELIST_PTR InsertErrorLog(ELIST_PTR list, char *data)
  263. {
  264. ELIST_PTR temp;
  265. static ELIST_PTR ende;
  266.  
  267.    if (NULL == (temp = (ELIST_PTR)malloc(sizeof(ELIST_REC))))
  268.       EXITERROR("Not enough memory to add to ErrorLog\n");
  269.    if (NULL == (temp->data = (char *)malloc(sizeof(char) * (strlen(data) +1))))
  270.       EXITERROR("Not enough memory to add to ErrorLog\n");
  271.  
  272.    strcpy(temp->data, data);
  273.    temp->next = NULL;
  274.  
  275.    if (list == NULL)
  276.       return (ende = temp);
  277.  
  278.    ende->next = temp;
  279.    ende = ende->next;
  280.  
  281.    return(list);
  282. }
  283. #ifndef NODETAIL
  284. /*******************************/
  285. LIST_PTR InsertDetail(LIST_PTR list, GOPHER_PTR data)
  286. {
  287. LIST_PTR temp;
  288.  
  289.       if (NULL == (temp = (LIST_PTR)malloc(sizeof(LIST_REC))))
  290.          EXITERROR("Not enough memory to add to DetailList\n");
  291.       temp->data = data;
  292.       temp->next = list;
  293.       temp->hits = 1;
  294.       return(temp);
  295. }
  296. #endif
  297. /*******************************/
  298. /* Insert data into tree, if that element is already in the tree
  299.  * then increment the number of hits.  cmp is used to find the location
  300.  * in the tree.
  301.  */
  302.  
  303. NODE_PTR Insert(NODE_PTR tree, GOPHER_PTR data, int cmp(GOPHER_PTR a, GOPHER_PTR b))
  304. {
  305. int i;
  306.  
  307.    if (tree == NULL)
  308.    {
  309.       if (NULL == (tree = (NODE_PTR) malloc(sizeof(NODE_REC))))
  310.          EXITERROR("No memory for InsertHost\n");
  311.       tree->data = data;
  312.       tree->left = tree->right = NULL;
  313. #ifndef NODETAIL
  314.       tree->llist = InsertDetail(NULL, data);
  315. #endif
  316.       tree->hits = 1;
  317.       return(tree);
  318.    }
  319.  
  320.    i=cmp(data, tree->data);
  321.  
  322.    if (i > 0)
  323.       tree->right = Insert(tree->right, data, cmp);
  324.    else if (i<0) 
  325.       tree->left = Insert(tree->left, data, cmp);
  326.    else
  327.    {
  328.       tree->hits++;
  329. #ifndef NODETAIL
  330.       tree->llist = InsertDetail(tree->llist, data);
  331. #endif
  332.    }
  333.  
  334.    return(tree);
  335. }
  336. /*******************************/
  337. /* Insert dates into tree, if that element is already in the tree
  338.  * then increment the number of hits.  cmp is used to find the location
  339.  * in the tree.
  340.  */
  341.  
  342. NODE_PTR InsertDates(NODE_PTR tree, GOPHER_PTR data, int cmp(GOPHER_PTR a, GOPHER_PTR b))
  343. {
  344. int i;
  345. static NODE_PTR last;
  346. NODE_PTR temp;
  347.  
  348.    if (tree == NULL)
  349.    {
  350.       if (NULL == (tree = (NODE_PTR) malloc(sizeof(NODE_REC))))
  351.          EXITERROR("No memory for InsertHost\n");
  352.       tree->data = data;
  353.       tree->left = tree->right = NULL;
  354. #ifndef NODETAIL
  355.       tree->llist = InsertDetail(NULL, data);
  356. #endif
  357.       tree->hits = 1;
  358.       return(last = tree);
  359.    }
  360.  
  361.    i=cmp(data, last->data);
  362.  
  363.    if (i == 0)
  364.    {
  365.       last->hits++;
  366. #ifndef NODETAIL
  367.       last->llist = InsertDetail(last->llist, data);
  368. #endif
  369.    }
  370.    else
  371.    {
  372.       temp = last; /* because last is going to be reset */
  373.       temp->right = InsertDates(last->right, data, cmp);
  374.    }
  375.  
  376.    return(tree);
  377. }
  378. /*******************************/
  379. /* Return the node with the data in the tree, using the cmp routine */
  380. NODE_PTR Find(NODE_PTR tree, GOPHER_PTR data, int cmp(GOPHER_PTR a, GOPHER_PTR b))
  381. {
  382. int i;
  383.  
  384.    if (tree == NULL)
  385.       return (NULL);
  386.  
  387.    i=cmp(data, tree->data);
  388.  
  389.    if (i > 0)
  390.       return(Find(tree->right, data, cmp));
  391.    if (i<0) 
  392.       return(Find(tree->left, data, cmp));
  393.  
  394.    return(tree);
  395. }
  396. /*******************************/
  397. /* Get a single field from temp, and return the new spot */
  398. char *getf(char *temp, char *field)
  399. {
  400.    while(*temp == ' ')
  401.       temp++;
  402.  
  403.    *field = '\0';
  404.    if (*temp == '\n')
  405.       return(temp);
  406.  
  407.    while ((*temp != ' ') && (*temp != '\0'))
  408.      *field++ = *temp++;
  409.  
  410.    *field = '\0';
  411.    return(temp);
  412. }
  413. /*******************************/
  414. int TypesCmp(GOPHER_PTR a, GOPHER_PTR b)
  415. {
  416.    return(a->type - b->type); 
  417. }
  418. /*******************************/
  419. int TimesCmp(GOPHER_PTR a, GOPHER_PTR b)
  420.    return(a->hour - b->hour); 
  421. }
  422. /*******************************/
  423. int HostsCmp(GOPHER_PTR a, GOPHER_PTR b)
  424.    return(strcmp(a->hostname, b->hostname)); 
  425. }
  426. /*******************************/
  427. int DocsCmp(GOPHER_PTR a, GOPHER_PTR b)
  428.    return(strcmp(a->path, b->path)); 
  429. }
  430. /*******************************/
  431. int DaysCmp(GOPHER_PTR a, GOPHER_PTR b)
  432.    return(a->day - b->day); 
  433. }
  434. /*******************************/
  435. int DatesCmp(GOPHER_PTR a, GOPHER_PTR b)
  436. {
  437. int i = a->month - b->month;
  438.    if (i == 0)
  439.       return(a->date - b->date);
  440.    else
  441.       return(i); 
  442. }
  443. /*******************************/
  444. int DomainCmp(GOPHER_PTR a, GOPHER_PTR b)
  445.    return(strcmp(a->domainname, b->domainname)); 
  446. }
  447. /*******************************/
  448. byte MonthStr2Num(char *str)
  449. {
  450. static int i = 0;
  451.  
  452.    if (strcmp(Months[i], str) == 0)
  453.       return(i+1);
  454.  
  455.    for(i=0;i<12;i++)
  456.       if (strcmp(Months[i], str) == 0)
  457.      return(i+1);
  458.    return(13); 
  459. }
  460. /*******************************/
  461. byte DayStr2Num(char *str)
  462. {
  463. static int i = 0;
  464.  
  465.    if (strcmp(Days[i], str) == 0)
  466.       return(i+1);
  467.  
  468.    for(i=0;i<7;i++)
  469.       if (strcmp(Days[i], str) == 0)
  470.      return(i+1);
  471.    return(8); 
  472. }
  473. /*******************************/
  474. /* This routine adds all the data to all the trees.  Now that I have done
  475.  * it this way, I really hate it.  What was I thinking? :)  It makes no
  476.  * sense to have the dates sort be a tree, since it is always the worst
  477.  * case tree :(.  Oh well maybe in 4.0 someone will fix it 
  478.  */
  479. void AddData(GOPHER_PTR data)
  480. {
  481.    if ((data->month >= mbegin) && (data->month <= mend))
  482.    {
  483.       if (do_H)
  484.          hosts = Insert(hosts, data, HostsCmp);
  485.       if (do_D)
  486.          docs  = Insert(docs, data, DocsCmp);
  487.       dates = InsertDates(dates, data, DatesCmp); /* Always do dates */
  488.       if (do_T)
  489.          types = Insert(types, data, TypesCmp);
  490.       if (do_I)
  491.          hours = Insert(hours, data, TimesCmp); 
  492.       if (do_W)
  493.          dais  = Insert(dais, data, DaysCmp);
  494.       if (do_O)
  495.          domains  = Insert(domains, data, DomainCmp);
  496.       /* Small hack to have start and stop dates */
  497.       if (start_date[0] == '\0')
  498.       {
  499.          sprintf(start_date, "%s %s %d", Days[data->day-1], Months[data->month-1],
  500.             data->date);
  501.       }
  502.       sprintf(stop_date, "%s %s %d", Days[data->day-1], Months[data->month-1],
  503.          data->date);
  504.       TotalConnects++;
  505.    }
  506. }
  507.  
  508. /*******************************/
  509. /* What is a more efficent, but CLEAN (non hack) way to do this? */
  510. void MakeDomain(char *hostname, char *domainname)
  511. {
  512. char *temp = strchr(hostname, '.');
  513.  
  514.    if (temp != NULL)
  515.    {
  516.       MakeDomain(temp+1, domainname);
  517.       *temp = 0;
  518.       strcat(domainname, ".");
  519.       strcat(domainname, hostname);
  520.       *temp = '.';
  521.    }
  522.    else
  523.        strcpy(domainname, hostname);
  524. }
  525. /*******************************/
  526. /* Process a single line completely.  It checks to make sure it is a 
  527.  * Valid line, if not it inserts it into the cruft
  528.  */
  529. void ProcessLine(char *line)
  530. {
  531. GOPHER_PTR data;
  532. short len;
  533. char *temp, *temp2; /* Used to save line, incase it is needed for cruft */
  534. char junk[MAXGLINESIZE];
  535. char message1[MAXGLINESIZE];
  536. char message2[MAXGLINESIZE];
  537.  
  538.    if (NULL == (data = (GOPHER_PTR)malloc(sizeof(GOPHER_REC))))
  539.       EXITERROR("Not enough memory to process line. Sorry\n");
  540.  
  541.    temp = line;
  542.  
  543.    temp = getf(temp, junk); /* Day */
  544.    if (8 == (data->day = DayStr2Num(junk))) 
  545.    { /* Not a real day of week */
  546.       free(data);
  547.       cruft = InsertErrorLog(cruft, line);
  548.       return;
  549.    }
  550.    temp = getf(temp, junk); /* Month */
  551.    if (13 == (data->month = MonthStr2Num(junk)))
  552.    { /* Not a real month */
  553.       free(data);
  554.       cruft = InsertErrorLog(cruft, line);
  555.       return;
  556.    }
  557.    temp = getf(temp, junk); /* Date */
  558.    data->date = atoi(junk);
  559.    temp = getf(temp, junk); /* Time */
  560.    junk[3] = '\0';
  561.    data->hour = atoi(junk); /* Hour */
  562.    temp = getf(temp, junk); /* Year */
  563.    temp = getf(temp, junk); /* Process ID */
  564.    temp = getf(temp ,junk); /* Hostname */
  565.    if (junk[0] == ':')
  566.    { /* A colon in the hostfield...baaad */
  567.       free(data);
  568.       cruft = InsertErrorLog(cruft, line);
  569.       return;
  570.    } 
  571.  
  572. /* This one is for that annoying 0.0.0.* IP address then gets stuck
  573.  * in the log when someone is trying to access something you ain't got
  574.  */
  575.    if (strncmp(junk,"0.0.0", 5) == 0)
  576.    { 
  577.       free(data);
  578.       cruft = InsertErrorLog(cruft, line);
  579.       return;
  580.    } 
  581.  
  582.    len = strlen(junk);
  583.    if (NULL == (data->hostname = (char *)malloc(sizeof(char) * (len+1))))
  584.       EXITERROR("Not enough memory. Sorry\n");
  585.    if (NULL == (data->domainname = (char *)malloc(sizeof(char) * (len+1))))
  586.       EXITERROR("Not enough memory. Sorry\n");
  587. #ifndef HCASEMATTERS
  588.    mytolower(junk); /* Only change to all lower if wanted that way */
  589. #endif
  590.    strcpy(data->hostname, junk);
  591.  
  592.    /* Figure out Domain stuff */
  593.    if (isdigit(junk[0]))
  594.    {
  595.       strcpy(data->domainname, data->hostname);
  596.       temp2 = strrchr(data->domainname,'.');
  597.       *temp2 = 0;
  598.    }
  599.    else if (NULL == (temp2 = strchr(junk,'.')))
  600.       strcpy(data->domainname, data->hostname);
  601.    else
  602.       MakeDomain(temp2+1, data->domainname);
  603.  
  604.    temp = getf(temp, junk); /* this should make junk[0]=':' */
  605.    if (junk[0] != ':')
  606.    { /* Now we don't have a colon */
  607.       free(data->hostname);
  608.       free(data->domainname);
  609.       free(data);
  610.       cruft = InsertErrorLog(cruft, line);
  611.       return;
  612.    } 
  613.  
  614.    temp = getf(temp, message1);
  615.    temp = getf(temp, message2);
  616.    while((*temp == ' ') && (*temp != '\0'))
  617.        temp++;
  618.  
  619.    len = strlen(temp);
  620.    data->path = (char *)malloc(sizeof(char)*(len+1));
  621.    strcpy(data->path, temp);
  622.    data->path[len] = '\0';
  623.  
  624.    if (0 != len)
  625.    {
  626.       if (data->path[len-1] == '\n')
  627.          data->path[len-1] = '\0';
  628.    }
  629.  
  630.    if (strcmp(message1, "Root") == 0)
  631.    {
  632.       data->type = DIRTYPE;
  633.       free(data->path);
  634.       data->path = ROOTNAME;
  635.       AddData(data);
  636.    }
  637.    else if ((strcmp(message1, "retrieved") == 0) && (strcmp(data->path, "/") == 0))
  638.    {
  639.       data->type = DIRTYPE;
  640.       free(data->path);
  641.       data->path = ROOTNAME;
  642.       AddData(data);
  643.    }
  644.    else if (strcmp(message1, "search") == 0)
  645.    {
  646.       strcpy(junk, message2);
  647. #ifdef NOSEARCHTXT
  648. /*This finds and removes everything after the " for " in a search line*/
  649.       if (strncmp(data->path, "for ", 4) == 0)
  650.       {
  651.          /* We found it at the beginning of data->path */
  652.          temp = data->path;
  653.       }
  654.       else if (NULL == (temp = mystrstr(data->path, " for ")))
  655.       { /* No " for " in the search */
  656.          free(data->path);
  657.          free(data->hostname);
  658.          free(data->domainname);
  659.          free(data);
  660.          cruft = InsertErrorLog(cruft, line);
  661.          return;
  662.       }
  663.       *temp = '\0';
  664. #endif
  665.       strcat(junk, " "); /* There is at least one space here */
  666.       strcat(junk, data->path);
  667.       free(data->path);
  668.       data->path = (char *)malloc(sizeof(char)*(strlen(junk)+1));
  669.       strcpy(data->path, junk);
  670.       data->type = SEARCHTYPE;
  671.  
  672.       AddData(data);
  673.    }
  674.    /* changed from "ftp:",4 to "ftp",3 because of NO-PASV patch */
  675.    /* which uses ftp-vms: and ftp-nopasv to denote special ftp types */
  676.    else if (strncmp(message2, "ftp", 3) == 0)
  677.    {
  678.       strcpy(junk, data->path); /* Incase there was a space in the path */
  679.       free(data->path); /* Then we have to save off path, since it contains it*/
  680.       data->path = (char *)malloc(sizeof(char)*(strlen(message2)+strlen(junk)-2));
  681.       strcpy(data->path, message2+4);
  682.       if (strlen(junk) > 0)
  683.       {
  684.          strcat(data->path, " ");
  685.          strcat(data->path, junk);
  686.       }
  687.       data->type = FTPTYPE;
  688.  
  689.       AddData(data);
  690.    }
  691.    else if (strcmp(message1, "retrieved") == 0)
  692.    {
  693.       if (data->path[0] == '\0')
  694.       { /* We some how retrieved nothing */
  695.          free(data->path);
  696.          free(data->hostname);
  697.          free(data->domainname);
  698.          free(data);
  699.          cruft = InsertErrorLog(cruft, line);
  700.          return;
  701.       } 
  702.  
  703.       if (strcmp(message2, "directory") == 0)
  704.          data->type = DIRTYPE;
  705.       else if (strcmp(message2, "maildir") == 0)
  706.          data->type = MAILDIRTYPE;
  707.       else if (strcmp(message2, "file") == 0)
  708.          data->type = FILETYPE;
  709.       else if (strcmp(message2, "binary") == 0)
  710.          data->type = BINARYTYPE;
  711.       else if (strcmp(message2, "sound") == 0)
  712.          data->type = SOUNDTYPE;
  713.       else if (strcmp(message2, "range") == 0)
  714.          data->type = RANGETYPE;
  715.       else
  716.       {
  717.          free(data->path);
  718.          free(data->hostname);
  719.          free(data->domainname);
  720.          free(data);
  721.          cruft = InsertErrorLog(cruft, line);
  722.          return;
  723.       } 
  724.       AddData(data);
  725.  
  726.    }
  727.    else /* wasn't anything we know about, g+ maybe?*/
  728.    {
  729.       free(data->path);
  730.       free(data->hostname);
  731.       free(data->domainname);
  732.       free(data);
  733.       cruft = InsertErrorLog(cruft, line);
  734.       return;
  735.    } 
  736.  
  737.    return;
  738. }
  739.  
  740. /*******************************/
  741. /* The main loop for gathering the data from stdin */
  742. void GatherInfo(void)
  743. {
  744. char line[MAXGLINESIZE];
  745. FILE *fp;
  746.  
  747.    start_date[0] = '\0';
  748.  
  749.    if (in_file_name == NULL)
  750.       fp = stdin;
  751.    else if (NULL == (fp = fopen(in_file_name, "r")))
  752.       INFOERROR("Error opening input file %s\n", in_file_name);
  753.    while(!feof(fp))
  754.    {
  755.       fgets(line, MAXGLINESIZE, fp);
  756.       if (feof(fp))
  757.          break;
  758.       ProcessLine(line);
  759.    }
  760. }
  761. /*******************************/
  762. /* These vars are only valid right after a call to TreeTo?List.  I could have
  763.  *  done some fancy var passing, but why bother. :)  All the print/plot/graph
  764.  *  routines should use the G vars.  Which are modified for averages.
  765.  */
  766. LIST_PTR GByNum;
  767. long int GByNumMin; /* These two are used for histograms */
  768. long int GByNumMax;
  769. long int GNodes;
  770. long int GTotalConnects;
  771.  
  772. /*******************************/
  773. /* Insert the data and number of hits that data got, into a sorted (by hits)
  774.  * link list (GByNum).
  775.  */
  776. void InsertSByNum(GOPHER_PTR data, long int hits)
  777. {
  778. LIST_PTR temp, temp2;
  779.  
  780.    if (NULL == (temp = (LIST_PTR)malloc(sizeof(LIST_REC))))
  781.       EXITERROR("Not enough memory in InsertByNum\n");
  782.    temp->data = data;
  783.    temp->next = NULL;
  784.    temp->hits = hits;
  785.  
  786. /* Figure out some vars */
  787.    if (hits < GByNumMin)
  788.       GByNumMin = hits;
  789.    if (hits > GByNumMax)
  790.       GByNumMax = hits;
  791.    GNodes++;
  792.    
  793.    if (GByNum == NULL)
  794.       GByNum = temp;
  795.    else if ((GByNum->hits-hits)*SortMult < 0)
  796.    {
  797.       temp->next = GByNum;
  798.       GByNum = temp;
  799.    }
  800.    else
  801.    {
  802.       temp2 = GByNum;
  803.       while (temp2->next != NULL)
  804.       {
  805.          if ((temp2->next->hits-hits)*SortMult < 0)
  806.      {
  807.         temp->next = temp2->next;
  808.         temp2->next = temp;
  809.         return;
  810.      }
  811.      temp2 = temp2->next;
  812.       }
  813.       temp2->next = temp;
  814.    }
  815. }
  816.  
  817. /*******************************/
  818. /* Place data and hits onto the front of the list GByNum */
  819. void InsertUByNum(GOPHER_PTR data, long int hits)
  820. {
  821. LIST_PTR temp;
  822.  
  823.    if (NULL == (temp = (LIST_PTR)malloc(sizeof(LIST_REC))))
  824.       EXITERROR("Not enough memory in InsertUByNum\n");
  825.    temp->data = data;
  826.    temp->next = NULL;
  827.    temp->hits = hits;
  828.  
  829. /* Figure out some vars */
  830.    if (hits < GByNumMin)
  831.       GByNumMin = hits;
  832.    if (hits > GByNumMax)
  833.       GByNumMax = hits;
  834.    GNodes++;
  835.  
  836.    if (GByNum == NULL)
  837.       GByNum = temp;
  838.    else
  839.    {
  840.       temp->next = GByNum;
  841.       GByNum = temp;
  842.    }
  843. }
  844. /*******************************/
  845. /* I did two different routines so it would be faster :).  I know this
  846.  * doesn't follow the logic of the rest of the program, but oh well.
  847.  * Do Inorder so that they remain in order, if two have the same
  848.  * num of hits.  S<orted> mean use InsertSByNum. U<nsorted> means use
  849.  * InsertUByNum.
  850.  */
  851. void TreeToSList(NODE_PTR tree)
  852. {
  853.    if (tree == NULL)
  854.       return;
  855.    
  856.    TreeToSList(tree->left);
  857.    InsertSByNum(tree->data, tree->hits);
  858.    TreeToSList(tree->right);
  859. }
  860.  
  861. /*******************************/
  862. /* See above :) */
  863. void TreeToUList(NODE_PTR tree)
  864. {
  865.    if (tree == NULL)
  866.       return;
  867.    
  868.    TreeToUList(tree->right);
  869.    InsertUByNum(tree->data, tree->hits);
  870.    TreeToUList(tree->left);
  871. }
  872. /*******************************/
  873. /* See above :) R = Reverse */
  874. void TreeToRUList(NODE_PTR tree)
  875. {
  876.    if (tree == NULL)
  877.       return;
  878.    
  879.    TreeToRUList(tree->left);
  880.    InsertUByNum(tree->data, tree->hits);
  881.    TreeToRUList(tree->right);
  882. }
  883.  
  884. /*******************************/
  885. NODE_PTR ListToTree(LIST_PTR list, int cmp(GOPHER_PTR, GOPHER_PTR))
  886. {
  887. NODE_PTR temptree = NULL;
  888.    for(;list != NULL; list = list->next)
  889.       temptree = Insert(temptree, list->data, cmp);
  890.    return(temptree);
  891. }
  892. /*******************************/
  893. void FreeList(LIST_PTR list)
  894. {
  895. LIST_PTR temp;
  896.    while (list != NULL)
  897.    {
  898.       temp = list;
  899.       list = list->next;
  900.       free(temp);
  901.    }
  902. }
  903. /*******************************/
  904. void FreeTree(NODE_PTR tree)
  905. {
  906.    if (tree == NULL)
  907.       return;
  908.    FreeTree(tree->left);
  909.    FreeTree(tree->right);
  910. #ifndef NODETAIL
  911.    FreeList(tree->llist);
  912. #endif
  913.    free(tree);
  914.    return;
  915. }
  916. /*******************************/
  917. int SizeTree(NODE_PTR tree)
  918. {
  919.    if (tree == NULL)
  920.       return 0;
  921.    return (1 + SizeTree(tree->left) + SizeTree(tree->right));
  922. }
  923. /*******************************/
  924. /* Given a string and and len, left justify and fill with fill */
  925. void printl(char *str, int len, char fill)
  926. {
  927.    while (len > 0)
  928.    {
  929.       if (*str == '\n')
  930.          str++;
  931.       if (*str == '\0')
  932.      putc(fill, stdout);
  933.       else
  934.          putc(*str++, stdout);
  935.       len--;
  936.    }
  937. }
  938. /*******************************/
  939. /* Given a string center justify and fill with fill */
  940. void printc(char *str, char fill)
  941. {
  942. int i, len = strlen(str);
  943. int start = (Width + WIDTHSUB - len)/2;
  944.  
  945.    for(i = 0; i < start; i++)
  946.       putc(fill, stdout);
  947.  
  948.    fputs(str, stdout);
  949.  
  950.    if (fill != ' ')
  951.       for(i = start+len; i < Width + WIDTHSUB ; i++)
  952.          putc(fill, stdout);
  953.    putc('\n', stdout);
  954. }
  955. /*******************************/
  956. char *TypeNames(char type)
  957. {
  958.    switch(type)
  959.    {
  960.    case FILETYPE:
  961.       return("File");
  962.    case SOUNDTYPE:
  963.       return("Sound");
  964.    case BINARYTYPE:
  965.       return("Binary File");
  966.    case DIRTYPE:
  967.       return("Directory");
  968.    case MAILDIRTYPE:
  969.       return("Mail Directory");
  970.    case FTPTYPE:
  971.       return("FTP");
  972.    case RANGETYPE:
  973.       return("Range");
  974.    case SEARCHTYPE:
  975.       return("Search");
  976.    }
  977.    return("Unknown");
  978. }
  979. /*******************************/
  980. void PrintData(GOPHER_PTR data)
  981. {
  982.    if (data == STARTINFO)
  983.          printf("Data access%s:\n", AverageStrs[AverageStrPos]);
  984.    else if (data == ENDINFO)
  985.       printf("%ld nodes accessed.  Average of %ld connections per node.\n",
  986.          GNodes, GTotalConnects/GNodes);
  987.    else
  988.    {
  989.       printf("%c ",data->type);
  990.       printl(data->path, Width - 2, ' ');
  991.    }
  992. }
  993. /*******************************/
  994. void PrintTime(GOPHER_PTR data)
  995. {
  996.    if (data == STARTINFO)
  997.       printf("Times%s:\n", AverageStrs[AverageStrPos]);
  998.    else if (data == ENDINFO)
  999.       printf("Average of %ld connections per hour.\n",
  1000.          GTotalConnects/GNodes);
  1001.    else
  1002.    {
  1003.       printf("%2d:00",data->hour);
  1004.       printl("", Width - 5, ' ');
  1005.    }
  1006. }
  1007. /*******************************/
  1008. void PrintType(GOPHER_PTR data)
  1009. {
  1010.    if (data == STARTINFO)
  1011.       printf("Types%s:\n", AverageStrs[AverageStrPos]);
  1012.    else if (data == ENDINFO)
  1013.       printf("Average of %ld connections per type.\n",
  1014.          GTotalConnects/GNodes);
  1015.    else
  1016.       printl(TypeNames(data->type), Width, ' ');
  1017. }
  1018. /*******************************/
  1019. void PrintHost(GOPHER_PTR data)
  1020. {
  1021.    if (data == STARTINFO)
  1022.       printf("Hosts%s:\n", AverageStrs[AverageStrPos]);
  1023.    else if (data == ENDINFO)
  1024.       printf("%ld hosts connected.  Average of %ld connections per host.\n",
  1025.          GNodes, GTotalConnects/GNodes);
  1026.    else
  1027.       printl(data->hostname, Width, ' ');
  1028.  
  1029. }
  1030. /*******************************/
  1031. void PrintDomain(GOPHER_PTR data)
  1032. {
  1033.    if (data == STARTINFO)
  1034.       printf("Domains%s:\n", AverageStrs[AverageStrPos]);
  1035.    else if (data == ENDINFO)
  1036.       printf("%ld domains connected.  Average of %ld connections per domain.\n",
  1037.          GNodes, GTotalConnects/GNodes);
  1038.    else
  1039.       printl(data->domainname, Width, ' ');
  1040.  
  1041. }
  1042. /*******************************/
  1043. void PrintDay(GOPHER_PTR data)
  1044. {
  1045.    if (data == STARTINFO)
  1046.       printf("Days%s:\n", AverageStrs[AverageStrPos]);
  1047.    else if (data == ENDINFO)
  1048.       printf("Average of %ld connections per day of week.\n",
  1049.          GTotalConnects/GNodes);
  1050.    else
  1051.       printl(Days[data->day-1], Width, ' ');
  1052. }
  1053. /*******************************/
  1054. void PrintDate(GOPHER_PTR data)
  1055. {
  1056.    if (data == STARTINFO)
  1057.       printf("Dates %s:\n", AverageStrs[AverageStrPos]);
  1058.    else if (data == ENDINFO)
  1059.    {
  1060.       printf("Connections occured on %ld different days.\n", GNodes);
  1061.       printf("Average of %ld connections per day.\n", GTotalConnects/GNodes);
  1062.    }
  1063.    else
  1064.    {
  1065.       
  1066.       printf("%3s %3s %d", Days[data->day-1], Months[data->month-1], data->date);
  1067.       if (data->date < 10)
  1068.          printl("\0", Width - 9, ' ');
  1069.       else
  1070.          printl("\0", Width - 10, ' ');
  1071.    }
  1072. }
  1073. /*******************************/
  1074. void PlotData(FILE *rfp, long int num, GOPHER_PTR data)
  1075. {
  1076.    if (data == STARTINFO)
  1077.       INFOERROR("Plot of Data is not currently supported. Mail me ideas: %s\n", EMAIL);
  1078. }
  1079. /*******************************/
  1080. void PlotType(FILE *rfp, long int num, GOPHER_PTR data)
  1081. {
  1082. char *temp = NULL;
  1083.    if (data == STARTINFO)
  1084.    {
  1085.       fprintf(rfp,"set xtics (");
  1086.    }
  1087.    else if (data == ENDINFO)
  1088.    {
  1089.       fprintf(rfp,"\"\" %ld)\n", num);
  1090.       fprintf(rfp,"set data style linespoints\n");
  1091.       fprintf(rfp,"set tics out\n");
  1092.       fprintf(rfp,"set grid\n");
  1093.       fprintf(rfp,"set title \"Gopher Usage\"\n");
  1094.       fprintf(rfp,"plot \"%s.dat\"\n", base);
  1095.    }
  1096.    else
  1097.    {
  1098.       temp = TypeNames(data->type);
  1099.       fprintf(rfp,"\"%s\" %ld,", temp, num);
  1100.    }
  1101.  
  1102. }
  1103. /*******************************/
  1104. void PlotHost(FILE *rfp, long int num, GOPHER_PTR data)
  1105. {
  1106.    if (data == STARTINFO)
  1107.       INFOERROR("Plot of Hosts is not currently supported.  Mail me ideas: %s\n", EMAIL);
  1108. }
  1109. /*******************************/
  1110. void PlotDay(FILE *rfp, long int num, GOPHER_PTR data)
  1111. {
  1112.    if (data == STARTINFO)
  1113.    {
  1114.       fprintf(rfp,"set xtics (");
  1115.    }
  1116.    else if (data == ENDINFO)
  1117.    {
  1118.       fprintf(rfp,"\"\" %ld)\n", num);
  1119.       fprintf(rfp,"set data style linespoints\n");
  1120.       fprintf(rfp,"set tics out\n");
  1121.       fprintf(rfp,"set grid\n");
  1122.       fprintf(rfp,"set title \"Gopher Usage\"\n");
  1123.       fprintf(rfp,"plot \"%s.dat\"\n", base);
  1124.    }
  1125.    else
  1126.    {
  1127.       fprintf(rfp,"\"%s\" %ld,",Days[data->day-1], num);
  1128.    }
  1129. }
  1130. /*******************************/
  1131. void PlotTime(FILE *rfp, long int num, GOPHER_PTR data)
  1132. {
  1133.    if (data == STARTINFO)
  1134.    {
  1135.       fprintf(rfp,"set xtics (");
  1136.    }
  1137.    else if (data == ENDINFO)
  1138.    {
  1139.       fprintf(rfp,"\"\" %ld)\n", num);
  1140.       fprintf(rfp,"set data style linespoints\n");
  1141.       fprintf(rfp,"set tics out\n");
  1142.       fprintf(rfp,"set grid\n");
  1143.       fprintf(rfp,"set title \"Gopher Usage\"\n");
  1144.       fprintf(rfp,"plot \"%s.dat\"\n", base);
  1145.    }
  1146.    else
  1147.    {
  1148.       fprintf(rfp,"\"%2d\" %ld,",data->hour, num);
  1149.    }
  1150. }
  1151. /*******************************/
  1152. void PlotDate(FILE *rfp, long int num, GOPHER_PTR data)
  1153. {
  1154.    if (data == STARTINFO)
  1155.    {
  1156.       fprintf(rfp,"set xtics (");
  1157.    }
  1158.    else if (data == ENDINFO)
  1159.    {
  1160.       fprintf(rfp,"\"\" %ld)\n", num);
  1161.       fprintf(rfp,"set data style linespoints\n");
  1162.       fprintf(rfp,"set tics out\n");
  1163.       fprintf(rfp,"set grid\n");
  1164.       fprintf(rfp,"set title \"Gopher Usage\"\n");
  1165.       fprintf(rfp,"plot \"%s.dat\"\n", base);
  1166.    }
  1167.    else
  1168.    {
  1169.       if ((data->date == 1) || (data->date == 15) || (num == 1))
  1170.          fprintf(rfp,"\"%s/%d\" %ld,",Months[data->month-1], data->date, num);
  1171.    }
  1172. }
  1173. #ifndef NODETAIL
  1174. /*******************************/
  1175. void DoDetail(NODE_PTR tree, byte DetailType)
  1176. {
  1177. NODE_PTR newtree;
  1178.    switch(DetailType)
  1179.    {
  1180.    case DATAINFO:
  1181.       newtree = ListToTree(tree->llist, DocsCmp);
  1182.       PrintInfo(newtree, PrintData, DocsCmp, DETAILINFO);
  1183.       break;
  1184.    case HOSTINFO:
  1185.       newtree = ListToTree(tree->llist, HostsCmp);
  1186.       PrintInfo(newtree, PrintHost, HostsCmp, DETAILINFO);
  1187.       break;
  1188.    case WEEKDAYINFO:
  1189.       newtree = ListToTree(tree->llist, DaysCmp);
  1190.       PrintInfo(newtree, PrintDay, DaysCmp, DETAILINFO);
  1191.       break;
  1192.    case MONTHDATEINFO:
  1193.       newtree = ListToTree(tree->llist, DatesCmp);
  1194.       PrintInfo(newtree, PrintDate, DatesCmp, DETAILINFO);
  1195.       break;
  1196.    case TYPEINFO:
  1197.       newtree = ListToTree(tree->llist, TypesCmp);
  1198.       PrintInfo(newtree, PrintType, TypesCmp, DETAILINFO);
  1199.       break;
  1200.    case TIMEINFO:
  1201.       newtree = ListToTree(tree->llist, TimesCmp);
  1202.       PrintInfo(newtree, PrintTime, TimesCmp, DETAILINFO);
  1203.       break;
  1204.    case DOMAININFO:
  1205.       newtree = ListToTree(tree->llist, DomainCmp);
  1206.       PrintInfo(newtree, PrintDomain, DomainCmp, DETAILINFO);
  1207.       break;
  1208.    default:
  1209.       newtree = NULL;
  1210.       break;
  1211.    }
  1212.    FreeTree(newtree);
  1213. }
  1214. #endif
  1215. /*******************************/
  1216. void PrintInfo(NODE_PTR tree, void print(GOPHER_PTR), int cmp(GOPHER_PTR a, GOPHER_PTR b), byte DetailType)
  1217. {
  1218. LIST_PTR temp;
  1219. LIST_PTR ByNum;
  1220. int Nodes;
  1221. long OldTotalConnects = TotalConnects;
  1222.  
  1223.    GByNum = NULL; /* Init the vars for the TreeToList function */
  1224.    GNodes = 0;
  1225.    if (SortHits)
  1226.       TreeToSList(tree);
  1227.    else if (SortMult == 1)
  1228.       TreeToUList(tree);
  1229.    else 
  1230.       TreeToRUList(tree);
  1231.  
  1232.    if (DetailType != DETAILINFO)
  1233.    { /* We are not printing Detail info now, so do headers */
  1234.       print(STARTINFO);
  1235.       printc("", '=');
  1236.    }
  1237.  
  1238.    ByNum = GByNum; /* Save off and clear the globals vars */
  1239.    Nodes = GNodes;
  1240.    GByNum = NULL;
  1241.  
  1242.    temp = ByNum;
  1243.    while (temp != NULL)
  1244.    {
  1245. #ifndef NODETAIL
  1246.       if (DetailType == DETAILINFO)
  1247.          printf("   ");
  1248. #endif
  1249.  
  1250.       print(temp->data);
  1251.       printf(" %4ld (%2.2f%%)\n", (long int)(temp->hits/AverageDiv), (float)temp->hits*100.0/TotalConnects);
  1252. #ifndef NODETAIL
  1253.       if ((DetailType != NOINFO) && (DetailType != DETAILINFO))
  1254.       {
  1255. #ifndef DETAIL_TOTAL
  1256.      TotalConnects = temp->hits;
  1257. #endif
  1258.          DoDetail(Find(tree, temp->data, cmp), DetailType);
  1259.      /* Don't generate Detail for NOINFO or if we are already doing detail */
  1260.          TotalConnects = OldTotalConnects;
  1261.       }
  1262. #endif
  1263.       temp = temp->next;
  1264.    }
  1265.    if (DetailType != DETAILINFO)
  1266.    { /* We are not printing Detail info now, so do footers */
  1267.       printf("\n");
  1268.       GNodes = Nodes; /* Restore, incase detail messed it up */
  1269.       GTotalConnects = TotalConnects/AverageDiv;
  1270.       print(ENDINFO);
  1271.    }
  1272.    printf("\n");
  1273.    FreeList(ByNum);
  1274. }
  1275. /*******************************/
  1276. void PlotInfo(NODE_PTR tree, void plot(FILE *, long int, GOPHER_PTR))
  1277. {
  1278. LIST_PTR temp;
  1279. FILE *rfp, *dfp;
  1280. char *fn;
  1281. long int points = 1;
  1282.  
  1283.    fn = (char *)malloc(strlen(base) + 5);
  1284.    sprintf(fn,"%s.run", base);
  1285.    if (NULL == (rfp = fopen(fn, "w")))
  1286.    {
  1287.       fprintf(stderr, "Could not open file \"%s\" for plot run\n", fn);
  1288.       free(fn);
  1289.       return;
  1290.    }
  1291.    sprintf(fn,"%s.dat", base);
  1292.    if (NULL == (dfp = fopen(fn, "w")))
  1293.    {
  1294.       fprintf(stderr, "Could not open file \"%s\" for plot data\n", fn);
  1295.       free(fn);
  1296.       return;
  1297.    }
  1298.    free(fn);
  1299.  
  1300.    plot(rfp, 0, STARTINFO);
  1301.    GByNum = NULL; /* Init the vars for the TreeToList function */
  1302.    GByNumMax = 0;
  1303.    GByNumMin = 36000;
  1304.    TreeToUList(tree);
  1305.  
  1306.    temp = GByNum;
  1307.    while (temp != NULL)
  1308.    {
  1309.       plot(rfp, points, temp->data);
  1310.       fprintf(dfp, "%ld %ld\n", points++, temp->hits);
  1311.       temp = temp->next;
  1312.    }
  1313.    plot(rfp, points, ENDINFO);
  1314.    printf("\n");
  1315.    FreeList(GByNum);
  1316.    fclose(rfp);
  1317.    fclose(dfp);
  1318. }
  1319. /*******************************/
  1320. void GraphInfo(NODE_PTR tree, void graph(GOPHER_PTR), int strwidth)
  1321. {
  1322. LIST_PTR temp;
  1323. LIST_PTR ByNum;
  1324. int Nodes;
  1325. int i, max, oldwidth = Width, MyWidth;
  1326.  
  1327.  
  1328.    GByNum = NULL; /* Init the vars for the TreeToList function */
  1329.    GNodes = 0;
  1330.    GByNumMax = 0;
  1331.    GByNumMin = 36000;
  1332.    if (SortHits)
  1333.       TreeToSList(tree);
  1334.    else if (SortMult == 1)
  1335.       TreeToUList(tree);
  1336.    else 
  1337.       TreeToRUList(tree);
  1338.  
  1339.    ByNum = GByNum; /* Save off and clear the globals vars */
  1340.    Nodes = GNodes;
  1341.    GByNum = NULL;
  1342.  
  1343.    graph(STARTINFO);
  1344.    printc("", '=');
  1345.  
  1346.    /* Do the width stuff after printc, yes its a major hack. :) */
  1347.    MyWidth = Width - strwidth;
  1348.    Width = strwidth;
  1349.  
  1350.    temp = ByNum;
  1351.    while (temp != NULL)
  1352.    {
  1353.       graph(temp->data);
  1354.       printf(" %4ld (%5.2f%%) ", (long int)(temp->hits/AverageDiv), (float)temp->hits*100.0/TotalConnects);
  1355.       max = (int)((temp->hits/AverageDiv) * (MyWidth/(float)GByNumMax*AverageDiv));
  1356.       for(i=0; i < max; i++)
  1357.      printf("#");
  1358.       printf("\n");
  1359.       temp = temp->next;
  1360.    }
  1361.    GNodes = Nodes; /* Restore */
  1362.    GTotalConnects = TotalConnects/AverageDiv;
  1363.    printf("\n");
  1364.    graph(ENDINFO);
  1365.    printf("\n");
  1366.    FreeList(ByNum);
  1367.    Width=oldwidth;
  1368. }
  1369. /*******************************/
  1370. void PrintErrorInfo(void)
  1371. {
  1372. ELIST_PTR temp = cruft;
  1373.  
  1374.    printf("Exception/Problem Report\n");
  1375.    printf("NOTE: THESE ENTRIES MAY DENOTE A SERVER PROBLEM. THEY SHOULD BE LOOKED OVER!\n");
  1376.    printc("", '=');
  1377.    while (temp != NULL)
  1378.    {
  1379.       printf(temp->data);
  1380.       temp = temp->next;
  1381.    }
  1382.    printf("\n");
  1383. }
  1384.  
  1385. /*******************************/
  1386.  
  1387. void PrintHelp(FILE *fp, int needreturn)
  1388. {
  1389.   fprintf(fp,"\nUsage: glog [<REPORTTYPE> | -%c | -h ] ", ERRORINFO);
  1390. #ifdef VMS
  1391.    fprintf(fp,"[OPTIONS] -i INFILENAME [-o OUTFILENAME]\n");
  1392. #else
  1393.    fprintf(fp,"[OPTIONS] [-i INFILENAME] [-o OUTFILENAME]\n");
  1394. #endif
  1395.    fprintf(fp,"  -h prints this help information\n");
  1396.    fprintf(fp,"  -%c displays an ERROR LOG\n", ERRORINFO);
  1397.    fprintf(fp,"  -i INFILENAME specifies the input file (your gopher logfile)\n");
  1398. #ifndef VMS
  1399.    fprintf(fp,"     stdin is expected to be your gopher logfile, unless -i is used\n");
  1400. #endif
  1401.    fprintf(fp,"  -o OUTFILENAME specifies the output file.  If an output file\n");
  1402.    fprintf(fp,"     is not specified, output will be to the screen.\n\n");
  1403.    
  1404.    fprintf(fp,"REPORTTYPE is:\n");
  1405.    fprintf(fp,"   -g<OUTPUTTYPE> for a histogram\n");
  1406.    fprintf(fp,"   -p<OUTPUTTYPE> to output plot data (gnuplot required to display plot)\n");
  1407. #ifndef NODETAIL
  1408.    fprintf(fp,"   -r<OUTPUTTYPE>[<OUTPUTTYPE>] for a report [with detail]\n\n");
  1409. #else
  1410.    fprintf(fp,"   -r<OUTPUTTYPE> for a report\n\n");
  1411. #endif
  1412.    
  1413. #ifdef VMS
  1414.    fprintf(fp,"OUTPUTTYPE is: (must be inclosed in quotes)\n");
  1415. #else
  1416.    fprintf(fp,"PUTPUTTYPE is:\n");
  1417. #endif
  1418.    fprintf(fp,"   %c = Host Names       %c = Day of Week \n",
  1419.       HOSTINFO, WEEKDAYINFO);
  1420.    fprintf(fp,"   %c = Document Names   %c = Month/Day\n",
  1421.     DATAINFO, MONTHDATEINFO);
  1422.    fprintf(fp,"   %c = Type             %c = Time\n", TYPEINFO, TIMEINFO);
  1423.    fprintf(fp,"   %c = Domain Name\n\n", DOMAININFO);
  1424.    if (needreturn)
  1425.    {
  1426.       fprintf(stderr, "PRESS [ENTER] TO CONTINUE\n");
  1427.       fflush(stderr);
  1428.       getc(stdin);
  1429.    }
  1430.    fprintf(fp,"OPTIONS are\n");
  1431.    fprintf(fp,"   [-b <beginning month #>] [-e <ending month #>]\n");
  1432.    fprintf(fp,"   [-w <width of report>] [-f <base filename for plot>]\n");
  1433.    fprintf(fp,"   [-a<AVERAGETYPE>] [-sSORTTYPE\n\n");
  1434. #ifdef VMS
  1435.    fprintf(fp,"AVERAGETYPE is: (must be inclosed in quotes)\n");
  1436. #else
  1437.    fprintf(fp,"AVERAGETYPE is:\n");
  1438. #endif
  1439.    fprintf(fp,"   E = Everything       Y = Per Year\n");
  1440.    fprintf(fp,"   M = Per Month        W = Per Week\n");
  1441.    fprintf(fp,"   D = Per Day          H = Per Hour\n\n");
  1442. #ifdef VMS
  1443.    fprintf(fp,"SORTTYPE is: (must be inclosed in quotes)\n");
  1444. #else
  1445.    fprintf(fp,"SORTTYPE is:\n");
  1446. #endif
  1447.    fprintf(fp,"   h = by hits forward   H = by Hits reverse\n");
  1448.    fprintf(fp,"   n = by name forward   N = by name reverse\n\n");
  1449.  
  1450.    fprintf(fp,"WARNING:  All arguments are evaluated from left to right,\n");
  1451.    fprintf(fp,"   except for -i, -o, -b, and -e.  Which are evaluated only at\n");
  1452.    fprintf(fp,"   the start of execution. This means forexample that the option\n");
  1453.    fprintf(fp,"   -sH has to be in the command line BEFORE a -rO, if you want\n");
  1454.    fprintf(fp,"   the sorting to be changed for the -rO report.\n\n");
  1455.  
  1456.  
  1457. #ifdef VMS
  1458.    fprintf(fp,"Example: glog -s\"n\" -g\"O\" -p\"T\" -a\"Y\" -s\"H\" -r\"D\" -i gopher.log\n");
  1459.    fprintf(fp,"   (Remember to enclose capital letters in double quotes!)\n");
  1460. #else
  1461.    fprintf(fp,"Example: glog34 -sn -gO -pT -aY -sH -rD < gopher.log | more\n");
  1462. #endif /* VMS */
  1463.    fprintf(fp," '-sn -gO' =  Print out a domain graph sorted by the domainname field\n");
  1464.    fprintf(fp," '-pT' = Generate a gnuplot file with type information. \n");
  1465.    fprintf(fp," '-aY -sH -rD' = Print out a Document Access Report sorted by\n");
  1466.    fprintf(fp,"  reverse number of hits, average the data as if there is a\n");
  1467.    fprintf(fp,"  years worth of data.\n");
  1468.  
  1469. #ifdef VMS
  1470.    fprintf(fp,"  Input is from gopher.log, output to the screen except for the plot data.\n");
  1471. #else
  1472.    fprintf(fp,"  Input is from gopher.log output to stdout, except for the plot\n");
  1473. #endif /* VMS */
  1474.  
  1475.    fflush(fp);
  1476. }
  1477.  
  1478. /*******************************/
  1479. void PrintHdr()
  1480. {
  1481. char center[80];
  1482.  
  1483.    printf("\n");
  1484.    printc(GLOG_VERSION,' ');
  1485.    fflush(stdout);
  1486.  
  1487.    sprintf(center, "%s to %s", start_date, stop_date);
  1488.    printc(center, ' ');
  1489.    if (AverageStrPos == 0)
  1490.    {
  1491.       sprintf(center, "%ld connections", TotalConnects);
  1492.       printc(center, ' ');
  1493.    }
  1494.    else
  1495.    {
  1496.       sprintf(center, "%ld real connections", TotalConnects);
  1497.       printc(center, ' ');
  1498.       printf("\n");
  1499.       sprintf(center, "%ld estimated connections%s", 
  1500.           (long int)(TotalConnects/AverageDiv), AverageStrs[AverageStrPos]);
  1501.       printc(center, ' ');
  1502.    }
  1503.    printf("\n\n");
  1504.    fflush(stdout);
  1505.    fflush(stderr);
  1506. }
  1507. /*******************************/
  1508. /* Yes I KNOW main is more than 1 page */
  1509. int main(int argc, char **argv)
  1510. int i;
  1511. int NumOfDays;
  1512.  
  1513. #ifdef THINK_C
  1514.     argc = ccommand(&argv); 
  1515. #endif   
  1516.  
  1517.    if  (1 == argc || argv[1][1] =='h')
  1518.    {
  1519.       PrintHelp(stdout,1); /* Clueless */
  1520.       exit(-1);
  1521.    }
  1522.  
  1523.    i = 1;
  1524. /* We must go through the arguments twice.  Once for the the arguments that
  1525.  * can only be set once.  I could add argument checking here, but why bother
  1526.  */
  1527.    while (i<argc)
  1528.    {
  1529.       if (argv[i][0] == '-')
  1530.       switch (argv[i][1])
  1531.       {
  1532.       case 'b':
  1533.          if (i<argc-1)
  1534.         mbegin = atoi(argv[++i]);
  1535.      else
  1536.         HELPERROR("\nexpected beginning month argument for -b\n");
  1537.          break;
  1538.       case 'e':
  1539.          if (i<argc-1)
  1540.         mend = atoi(argv[++i]);
  1541.      else
  1542.         HELPERROR("\nexpected ending month argument for -e\n");
  1543.          break;
  1544.       case 'i':
  1545.          if (i<argc-1)
  1546.         in_file_name = argv[++i];
  1547.      else
  1548.         HELPERROR("\nexpected input file name for -i\n");
  1549.          break;
  1550.       case 'o':
  1551.          if (i<argc-1)
  1552.          {
  1553.             i++;
  1554.             if (freopen(argv[i], "w", stdout) == NULL)
  1555.                INFOERROR("Unable to open requested output file %s\n", argv[i]);
  1556.          }
  1557.      else
  1558.         HELPERROR("\nexpected output file name for -o\n");
  1559.          break;
  1560.       case 'r':
  1561.       case 'p':
  1562.       case 'g':
  1563.          switch(argv[i][2])
  1564.      {
  1565.          case DATAINFO:
  1566.             do_D = 1;
  1567.             break;
  1568.          case TYPEINFO:
  1569.             do_T = 1;
  1570.             break;
  1571.          case TIMEINFO:
  1572.             do_I = 1;
  1573.             break;
  1574.          case WEEKDAYINFO:
  1575.             do_W = 1;
  1576.             break;
  1577.          case HOSTINFO:
  1578.             do_H = 1;
  1579.             break;
  1580.          case DOMAININFO:
  1581.             do_O = 1;
  1582.             break;
  1583.          }
  1584.      break;
  1585.       }
  1586.       i++;
  1587.    }
  1588.  
  1589. #ifdef VMS
  1590.       /* VMS barfs on stdin as input so FORCE filename */
  1591.       if (in_file_name == NULL)
  1592.          EXITERROR("No input file name on command line\n");
  1593. #endif /* VMS */
  1594.  
  1595.    GatherInfo();
  1596.  
  1597.    if (TotalConnects == 0)
  1598.       EXITERROR("\nNo [matching] data to process\n");
  1599.  
  1600.    NumOfDays = SizeTree(dates);
  1601.    fflush(stdout);
  1602.    fflush(stderr);
  1603.  
  1604.    i = 1;
  1605.    /* Now go through them again and actually do them, from left to right */
  1606.    while (i<argc)
  1607.    {
  1608.       switch (argv[i][1])
  1609.       {
  1610.       case 'a': /* average changes */
  1611.          switch(argv[i][2])
  1612.      {
  1613.      case 'E':
  1614.         AverageStrPos = 0;
  1615.         AverageDiv = 1.0;
  1616.         break;
  1617.      case 'Y':
  1618.         AverageStrPos = 1;
  1619.         AverageDiv = NumOfDays/365.0;
  1620.         break;
  1621.      case 'M':
  1622.         AverageStrPos = 2;
  1623.         AverageDiv = NumOfDays/(365.0/12.0);
  1624.         break;
  1625.      case 'W':
  1626.         AverageStrPos = 3;
  1627.         AverageDiv = NumOfDays/7.0;
  1628.         break;
  1629.      case 'D':
  1630.         AverageStrPos = 4;
  1631.         AverageDiv = NumOfDays;
  1632.         break;
  1633.      case 'H':
  1634.         AverageStrPos = 5;
  1635.         AverageDiv = NumOfDays*24.0;
  1636.         break;
  1637.      }
  1638.      break;
  1639.       case ERRORINFO:
  1640.      PrintErrorInfo();
  1641.      break;
  1642.       case 'r': /*custom reports*/
  1643.      PrintHdr();
  1644.          switch(argv[i][2])
  1645.      {
  1646.          case DATAINFO:
  1647.         PrintInfo(docs, PrintData, DocsCmp, argv[i][3]);
  1648.         break;
  1649.          case TYPEINFO:
  1650.         PrintInfo(types, PrintType, TypesCmp, argv[i][3]);
  1651.         break;
  1652.          case TIMEINFO:
  1653.         PrintInfo(hours, PrintTime, TimesCmp, argv[i][3]);
  1654.         break;
  1655.          case WEEKDAYINFO:
  1656.             PrintInfo(dais, PrintDay, DaysCmp, argv[i][3]);
  1657.         break;
  1658.          case MONTHDATEINFO:
  1659.         PrintInfo(dates, PrintDate, DatesCmp, argv[i][3]);
  1660.         break;
  1661.          case HOSTINFO:
  1662.            PrintInfo(hosts, PrintHost, HostsCmp, argv[i][3]);
  1663.         break;
  1664.          case DOMAININFO:
  1665.            PrintInfo(domains, PrintDomain, DomainCmp, argv[i][3]);
  1666.         break;
  1667.      default:
  1668.         HELPERROR("expected OUTPUTTYPE argument for -r\n");
  1669.         break;
  1670.      }
  1671.          printf(" \n");
  1672.      break;
  1673.       case 'p': /*custom plots*/
  1674.          switch (argv[i][2])
  1675.      {
  1676.          case DATAINFO:
  1677.             PlotInfo(docs, PlotData);
  1678.         break;
  1679.          case TYPEINFO:
  1680.         PlotInfo(types, PlotType);
  1681.         break;
  1682.          case TIMEINFO:
  1683.         PlotInfo(hours, PlotTime);
  1684.         break;
  1685.          case WEEKDAYINFO:
  1686.         PlotInfo(dais, PlotDay);
  1687.         break;
  1688.          case MONTHDATEINFO:
  1689.         PlotInfo(dates, PlotDate);
  1690.         break;
  1691.          case HOSTINFO:
  1692.         PlotInfo(hosts, PlotHost);
  1693.         break;
  1694.      default:
  1695.         HELPERROR("expected SORTTYPE argument for -p\n");
  1696.         break;
  1697.      }
  1698.       break;
  1699.       case 'g': /*custom graphs*/
  1700.      PrintHdr();
  1701.          switch (argv[i][2])
  1702.      {
  1703.          case DATAINFO:
  1704.             GraphInfo(docs, PrintData, 40);
  1705.         break;
  1706.          case TYPEINFO:
  1707.         GraphInfo(types, PrintType, 15);
  1708.         break;
  1709.          case TIMEINFO:
  1710.         GraphInfo(hours, PrintTime,15);
  1711.         break;
  1712.          case WEEKDAYINFO:
  1713.         GraphInfo(dais, PrintDay, 15);
  1714.         break;
  1715.          case MONTHDATEINFO:
  1716.         GraphInfo(dates, PrintDate,15);
  1717.         break;
  1718.          case HOSTINFO:
  1719.         GraphInfo(hosts, PrintHost, 35);
  1720.         break;
  1721.          case DOMAININFO:
  1722.         GraphInfo(domains, PrintDomain, 35);
  1723.         break;
  1724.      default:
  1725.         HELPERROR("expected SORTTYPE argument for -g\n");
  1726.         break;
  1727.      }
  1728.          printf(" \n");
  1729.       break;
  1730.       case 's':
  1731.      switch(argv[i][2])
  1732.      {
  1733.      case 'h':
  1734.         SortMult = 1;
  1735.         SortHits = 1;
  1736.         break;
  1737.      case 'H':
  1738.             SortMult = -1;
  1739.         SortHits = 1;
  1740.         break;
  1741.      case 'n':
  1742.         SortMult = 1;
  1743.         SortHits = 0;
  1744.         break;
  1745.      case 'N':
  1746.         SortMult = -1;
  1747.         SortHits = 0;
  1748.         break;
  1749.      default:
  1750.         HELPERROR("expected SORTTYPE argument for -g\n");
  1751.         break;
  1752.      }
  1753.      printf("SortMult = %d\n", SortMult);
  1754.      break;
  1755.       case 'w':
  1756.          if (i<argc-1)
  1757.         Width = atoi(argv[++i]) - WIDTHSUB;
  1758.      else
  1759.         HELPERROR("expected width argument for -w\n");
  1760.      break;
  1761.       case 'f':
  1762.          if (i<argc-1)
  1763.         base = argv[++i];
  1764.      else
  1765.         HELPERROR("expected filename argument for -f\n");
  1766.      break;
  1767.       case 'o':
  1768.       case 'i':
  1769.       case 'b':
  1770.       case 'e':
  1771.          i++;   /* Skip over their arguments */
  1772.          break; /* These are before GatherInfo arguments */
  1773.       case '?':
  1774.       default:
  1775.          fprintf(stderr, "Unknown option \"%c\" Use -h for help\n", argv[i][1]);
  1776.      break;
  1777.       } /*switch*/
  1778.     
  1779.       i++; /* next arg...*/
  1780.         
  1781.    } /*while*/
  1782.   
  1783.    exit(0);
  1784. }
  1785.  
  1786.  
  1787.